Many applications provide functionality for file uploading. For example, a content management system (CMS) may allow users to upload their own avatar and create blog posts with embedded files. There are also many other situations in which the nature of one's work necessitates file uploading, such as uploading of medical files, assignments or legal case files.
The first category of file upload vulnerabilities comprises the vulnerabilities which allow an adversary to upload executable files to the server. For example, if the server uses PHP, then such a vulnerability would allow an attacker to upload PHP files.
Once the malicious PHP file has been uploaded, the adversary can execute it by navigating to it or using curl
.
Consider the following file upload vulnerability from [this](https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload) PortSwigger lab. We have an unrestricted file upload and our goal is to read `/home/carlos/secret`. To achieve this, we simply need to paste the following code into an `exploit.php` file and upload it.
```php
<?php echo file_get_contents('/home/carlos/secret'); ?>
```
![Unrestricted File Upload](Resources/Images/File%20Upload/Unrestricted%20File%20Upload.png)
![Unrestricted File Upload Successful](Resources/Images/File%20Upload/Unrestricted%20File%20Upload%20Successful.png)
As we can see, the PHP script was uploaded to the `avatars/` directory. However, navigating directly to `avatars/exploit.php` results in a "Not Found" error. Let's go back to the `my-account` page and inspect the source of the avatar image.
![](Resources/Images/File%20Upload/Example%20-%20Inspect%20Source.png)
Ah, so our file was actually uploaded to `files/avatars/`. Navigating to this page results in the execution of `exploit.php`:
![](Resources/Images/File%20Upload/Example%20-%20Carlos%20Secret.png)
It may be possible to abuse a file upload to overwrite files on the server. One should always check what happens when they upload a file with the same name twice. If the application indicates whether the file existed previously, then this provides us with a way to brute force content on the server. If the server yields an error, this error may reveal interesting information about the underlying code of the web application. If neither of the these behaviours is observed, then the server might have simply overwritten the file.
This can sometimes be paired with a directory traversal vulnerability and may allow an adversary to overwrite sensitive files on the system such as by placing their own public key in the authorized_keys
of a user on the system, thereby granting themselves SSH access to the host.
Blindly overwriting files in an actual penetration test can result in serious data loss or costly downtime of a production system.
The third type of file upload vulnerabilities rely on user interaction such as waiting for a user to click on a .docx
file embedded with
Nowadays, virtually all web applications have some protection against file upload vulnerabilities but the defences put in place are not always particularly robust.
Sometimes an application trusts the client-side completely and only relies on the Content-Type
HTTP header to determine if the file really is legitimate or not.
However, an adversary is free to manipulate the Content-Type
header into anything they like. If the server relies solely on this field, then nothing will prevent an attacker from uploading a PHP reverse shell and just slapping an image/png
onto the Content-Type
header.
Many filters disallow specific file extensions such as .php
. Fortunately, these blacklists are rarely exhaustive and one can look for alternative extensions which still convey the same file type.
Many filters block the most common `.php` and `.phtml` extensions but do not block the less common ones like `.phps` and `.php7`.
Another way to bypass filters is to vary the case of the file extension, since a the server might only be checking against a lowercase extension. For example, the filter could block .php
but allow .pHp
.
Furthermore, some filters can be bypassed by using two extensions on the filename (exploit.jpg.php
) or by adding trailing characters such as dots or whitespaces (exploit.jpg.php.
).
Inserting semicolons or null bytes can also come in handy - exploit.php%00.jpg
or exploit.php;.jpg
. These usually arise when the validation code is written in a high-level language like PHP or Java, but the actual file is processed via a lower-level language like C/C++.
URL encoding dots, forward slashes and backslashes can also help with bypassing filters.
If the filename is filtered as a UTF-8 string but is then converted to ASCII when used as a path, one can use multi-byte Unicode characters which translate into two characters one of which is a dot (0x2e
) or a null bytes (0x00
) to bypass the filter.
Some defences involve the removal of file extensions which are considered dangerous. Oftentimes these are not recursive and will only check the string once. Therefore, the filename exploit.p.php.hp
will be turned into exploit.php
.
One should follow most if not all of the following practices in order to ensure that a file upload is secure:
Content-Type
header should not be trusted.